/**
 * Title: Hyperbolic engine
 * Description: jFamilyTree Engine
 * Version: provider
 * Copyright: (c) 2001-2008
 * Terms of use:see license agreement at http://treebolic.sourceforge.net/en/license.htm
 * Author: Bernard Bou
 * Company: bsys
 * Update: Mon Mar 10 00:00:00 CEST 2008
 */
package jFamilyTree.core;

/**
 * Hypercircle node location in hyperspace
 * 
 * @author Bernard Bou
 */
public class HyperLocation
{
	// C O N S T A N T S
	
	/**
	 * Where border starts (distance to origin) 
	 */
	static final private double BORDER = .95;

	// D A T A
	
	/**
	 * Center as computed by initial layout
	 */
	public final Complex center0;

	/**
	 * Center as mapped by current transform
	 */
	public final Complex center;

	/**
	 * Radius
	 */
	public double radius;

	/**
	 * Distance to (0,0)
	 */
	public double dist;

	// S T A T E
	
	/**
	 * Computation state (set whenever either center or radius or both change)
	 */
	public boolean isDirty;

	// B O R D E R

	/**
	 * Whether this hypercircle nears border
	 */
	public boolean isBorder;

	// C O N S T R U C T O R

	/**
	 * Construct hypercircle
	 */
	public HyperLocation()
	{
		center0 = new Complex();
		center = new Complex();
		radius = 0.;
		isBorder = false;
		isDirty = false;
	}

	// S E T

	/**
	 * Set hypercircle data
	 * 
	 * @param o
	 *        center
	 * @param r
	 *        radius
	 */
	public void set(Complex o, double r)
	{
		center0.set(o);
		center.set(o);
		dist = center.mag();
		isBorder = dist > BORDER;
		radius = r;
		isDirty = true;
	}

	/**
	 * Reset to untransformed initial position
	 */
	public void reset()
	{
		center.set(center0);
		dist = center.mag();
		isBorder = dist > BORDER;
		isDirty = true;
	}

	/**
	 * Clone
	 * 
	 * @param thisHyperLocation
	 *        hyper location
	 */
	public void clone(HyperLocation thisHyperLocation)
	{
		center0.set(thisHyperLocation.center0);
		center.set(thisHyperLocation.center);
		dist = thisHyperLocation.dist;
		isBorder = thisHyperLocation.isBorder;
		radius = thisHyperLocation.radius;
		isDirty = true;
	}

	// T R A N S F O R M

	/**
	 * Apply transform to this hypercircle
	 * 
	 * @param t
	 *        transform
	 */
	public void transform(IHyperTransform t)
	{
		// map
		t.map(center.set(center0));

		// distance to (0,0)
		dist = center.mag();

		// normalization (should not occur with proper transform)
		if (dist > 1.)
		{
			center.normalize();
			dist = center.mag();
		}

		// compute if off-limit
		isBorder = dist > BORDER;
		isDirty = true;
	}

	// H Y P E R B O L I C . D I S T A N C E

	/**
	 * Distance between 2 points
	 * 
	 * @param z1
	 *        point1
	 * @param z2
	 *        point2
	 * @return distance between two points
	 */
	static public double getHyperDistance(Complex z1, Complex z2)
	{
		// 2*atanh((z1-z2)/(1-z1*~z2));
		// 2*atanh(z) if distance from 0,0 origin
		Complex denom = new Complex().conj(z2).mul(z1).neg().add(Complex.ONE);
		Complex z = new Complex().sub(z1, z2).div(denom);
		return 2. * XMath.atanh(z.mag());
	}

	// S T R I N G

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#toString()
	 */
	public String toString()
	{
		return "o0=" + center0 + ", o=" + center + ", r=" + radius + ", d=" + dist;
	}
}
